<?php

namespace app\common\service\pay\wechat;

use app\common\service\pay\Base;
use app\common\service\pay\PayInterface;
use app\event\OrderRefundSuccess;
use app\event\PaySuccess;
use app\model\CouponOrder;
use app\model\PayLog;
use EasyWeChat\Factory;
use EasyWeChat\Payment\Application;
use think\Exception;
use think\facade\Db;
use think\facade\Log;
use think\facade\Request;

class WechatBase extends Base implements PayInterface
{

    public $refund_number;//商家退单号

    public function __construct()
    {
        $this->refund_number = time() . rand(10000, 99999);
        $this->app();
    }

    /**
     * @var Application
     */
    public $app;

    public $pay_channel = PayLog::PAY_CHANNEL_WECHAT;


    public function pay()
    {

        $formatData = $this->wechatFormat();

        $res = $this->app->order->unify($formatData);

        if (!isset($res['return_code']) || $res['return_code'] != 'SUCCESS') {
            Log::error($res['return_msg'] ?? '支付订单生成失败，请稍后再试。');
            return fail($res['return_msg'] ?? '支付订单生成失败，请稍后再试。');
        }

        return $res;

    }


    public function refund()
    {
        Log::info('到了退款');
        $params = $this->refundFormat();


        if ($this->couponOrder->refund_status != CouponOrder::C_REFUND_STATUS_ING) {
            throw new Exception('退款状态不正确');
        }


//        var_dump($params);exit;
        $res = $this->app->refund->byTransactionId($params['transactionId'], $params['refundNumber'], $params['totalFee'], $params['refundFee'], [
            // 可在此处传入其他参数，详细参数见微信支付文档
            'refund_desc' => $params['desc'],
        ]);

        if (empty($res['refund_id'])) {
            Log::error($res['err_code_des']);
            return false;
        }

        event(new OrderRefundSuccess($this->couponOrder));

    }


    /**
     * 退款格式化
     * @return array
     */
    public function refundFormat()
    {
        return [
            'transactionId' => $this->getTransactionId(),
            'refundNumber' => $this->getRefundNumber(),
            'totalFee' => $this->getTotalFee(),
            'refundFee' => $this->getRefundFee(),
            'desc' => $this->getRefundDesc(),
        ];
    }

    public function getTransactionId()
    {
        return $this->couponOrder->payLog->trade_no;
    }

    public function getRefundNumber()
    {
        return $this->refund_number;
    }

    public function getTotalFee()
    {
        return $this->couponOrder->payLog->real_pay_price;
    }

    public function getRefundFee()
    {
        return $this->total_fee;
    }

    public function getRefundDesc()
    {
        return '退款订单';
    }


    /**
     * 微信支付参数
     * @return array
     */
    public function wechatFormat()
    {

        $total_fee = isLocal() ? 1 : $this->real_pay_price; //非正式环境1分钱

        $res = [
            'body' => $this->title,
            'out_trade_no' => $this->payLog->out_no,
            'total_fee' => $total_fee,
            'spbill_create_ip' => request()->ip(),
            'trade_type' => $this->trade_type,
        ];

        return $res;
    }

    public function notify()
    {

        $params = request()->all();

        $wechat = new WechatBase();

        $app = $wechat->app;


        $response = $app->handlePaidNotify(function ($message, $fail) {

            /** @var \app\model\Notify $notify */

            $notify = new \app\model\Notify([
                'params' => $message,
                'from' => 'wechat',
                'relation_no' => $message['out_trade_no'],
            ]);

            $notify->save();


            $out_no = $message['out_trade_no'];
            /** @var PayLog $pay */
            $pay = PayLog::query()
                ->where('out_no', $out_no)
                ->find();


            if (empty($pay)) {
                return $fail('通信失败，请稍后再通知我');
            }

            if ($pay->status == PayLog::STATUS_PAY) {  //已支付无需回调
                return true;
            }

            $pay->trade_no = $message['transaction_id'];
            $pay->real_pay_price = $message['total_fee'];

            //支付成功事件
            event(new PaySuccess($pay));

            //支付成功处理事件
            $notify->status = \app\model\Notify::STATUS_FINISH;
            $notify->save();

            return true;
        });


        Log::info($params);

        return $response->send(); // return $response;
    }

    /**
     * 初始化微信配置
     * 如果将来需要客户端 则参考 app/common/model/WxpayV2.php
     */
    public function app()
    {

        $info = Db::name('config')
            ->where('type', 'wechat')
            ->column('value', 'field');
        $rootPath = root_path();

        $this->config = [
            // 必要配置
            'app_id' => $info['app_id'],
            'mch_id' => $info['mch_id'],
            'key' => $info['mch_key'],

            'cert_path' => '', // XXX: 绝对路径！！！！
            'key_path' => '', // XXX: 绝对路径！！！！

            'notify_url' => Request::domain() . '/api/notifyV3/wxpayNotify',

            /*
             * 日志配置
             *
             * level: 日志级别, 可选为：
             *         debug/info/notice/warning/error/critical/alert/emergency
             * path：日志文件位置(绝对路径!!!)，要求可写权限
             */
            'log' => [
                'default' => 'prod', // 默认使用的 channel，生产环境可以改为下面的 prod
                'channels' => [
                    // 测试环境
                    'dev' => [
                        'driver' => 'daily',
                        'path' => $rootPath . 'runtime/wxpay_v3/wxpay.log',
                        'level' => 'debug',
                    ],
                    // 生产环境
                    'prod' => [
                        'driver' => 'daily',
                        'path' => $rootPath . 'runtime/wxpay_v3/wxpay.log',
                        'level' => 'info',
                    ],
                ],
            ],
        ];
        if (!empty($info['cert_path'])) {
            $certRoot = app()->getRootPath() . 'data/';
            $this->config['cert_path'] = $certRoot . $info['cert_path'];
            $this->config['key_path'] = $certRoot . $info['key_path'];
        }

        $this->app = Factory::payment($this->config);
    }


}